home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
201-225
/
disk_214
/
memdiag
/
memory.doc
< prev
next >
Wrap
Text File
|
1992-05-06
|
7KB
|
159 lines
Memory Management on the Amiga
by Fabbian G. Dufoe, III
Because AmigaDOS is a multi-tasking operating system it requires a more
complicated memory management scheme than most personal computer operating
systems have. Single-tasking systems can get by with setting the upper and
lower limits of the free memory region. With different processes starting
and stopping independently free memory gets fragmented. The system has to
know where to find each fragment if it's to use memory efficiently.
AmigaDOS keeps a list of available memory. When a process needs RAM it
asks the operating system to allocate some. The system searches the memory
free list to see if there is a big enough block available. If so it
allocates it to the process and removes it from the memory free list. When
the process frees the memory the system returns it to the memory free list.
The operating system doesn't keep track of which process allocated the
memory, so if a process fails to return it there is no way to recover it
without rebooting the system.
Like everything in the Amiga's operating system, the path to the memory
free list begins with a pointer to the exec library ExecBase structure. The
ExecBase structure and all the structures and lists the system uses for
memory management are defined in the include files listed in the ROM Kernel
Exec Manual. These same files are supplied with your C compiler or
assembler. They are also available in the Native Developer's Kit available
>from Commodore Amiga Technical Support (CATS).
Opening the exec library returns a pointer to the ExecBase structure:
struct ExecBase *ExecBase;
ExecBase = (struct ExecBase *)OpenLibrary("exec.library, 0L);
The ExecBase structure is defined in exec/execbase.h. One of the
fields in the ExecBase structure is MemList. It's a List structure that
contains pointers to the list of memory regions which make up the memory
free list. There is a MemList structure documented in the ROM Kernel Manual
and in exec/memory.h. It is not related to the List structure named MemList
in the ExecBase structure. The similar names could easily confuse you.
Exec/lists.h defines a List structure as follows:
struct List {
struct Node *lh_Head;
struct Node *lh_Tail;
struct Node *lh_TailPred;
UBYTE lh_Type;
UBYTE l_pad;
};
The Node structure is defined in exec/nodes.h as follows:
struct Node {
struct Node *ln_Succ;
struct Node *ln_Pred;
UBYTE ln_Type;
BYTE ln_Pri;
char *ln_Name;
};
The memory free list is organized as a list of lists. MemList points
to a list of memory regions. A region is a block of contiguous memory from
which Exec can draw memory. Each region is described by a MemHeader
structure. When the system is initialized it creates a region for chip RAM
and one for each expansion RAM board in the system.
If the expansion RAM boards occupy contiguous memory you can combine
their regions into one with a program called MergeMem. MergeMem is in the
System directory of the 1.3 Workbench disk.
The MemHeader structure is documented in exec/memory.h as follows:
struct MemHeader {
struct Node mh_Node;
UWORD mh_Attributes; /* characteristics of this region */
struct MemChunk *mh_First; /* first free region */
APTR mh_Lower; /* lower memory bound */
APTR mh_Upper; /* upper memory bound+1 */
ULONG mh_Free; /* total number of free bytes */
};
So the following gives the address of the first region in the memory
free list:
struct MemHeader *MemHeader;
MemHeader = (struct MemHeader *)ExecBase->MemList.lh_Head;
The MemHeader structure begins with a Node structure (mh_Node) which
links this region to the others in the system's memory free list. Within
the Node structure mh_Node.ln_Succ points to the next region and
mh_Node.ln_Pred points to the preceding one. In the first node in the list
mh_Node.ln_Pred points to the head of the list (MemHeader == MemHeader-
>mh_Node.ln_Pred).
The MemHeader structure defines the upper (mh_Upper) and lower limits
(mh_Lower) of the region and tells how many bytes it currently has available
(mh_Free). It contains a pointer to the first chunk of free memory
(mh_First).
Memory chunks are organized as a singly linked list. The NewChunk
structure (documented in exec/memory.h) contains the address of the next
chunk in mc_Next and the number of bytes in this chunk in mc_Bytes:
struct MemChunk {
struct MemChunk *mc_Next; /* pointer to next chunk */
ULONG mc_Bytes; /* chunk byte size */
};
A NULL pointer in mc_Next marks the end of the list.
Before any memory is allocated from a region there will be just one
chunk in the list. The fields mh_First and mh_Lower will be equal and
mh_Upper will be the same as mh_Free. The smallest amount of memory that
can be allocated is eight bytes. That's because the smallest chunk of
memory that can be replaced on the memory free list is eight bytes. A
MemChunk requires four bytes for mc_Next (a 32-bit address) and four bytes
for mc_Bytes (an unsigned long integer).
When the first block of memory is allocated from a region the size of
the block is added to mh_First and mh_Free is reduced by the size of the
block. In the MemChunk structure mc_Next remains NULL and mc_Bytes is
reduced by the amount of memory allocated.
If a second block is allocated from the same region the size of the
second block will be added to mh_First and subtracted from mh_Free and
mc_Bytes. There will still be just one chunk in the list, although it will
be smaller.
The first fragmentation of the region occurs when the first block is
freed while the second remains allocated. Then mh_First will be restored to
its original value (equal to mh_Lower) and mh_Free will be increased by the
size of the block. The address pointed to by mh_First (which is mc_Next)
will be loaded with the previous value of mh_First. The size of the block
will be loaded into mc_Bytes for the first MemChunk structure.
Given Block1 as the size of the first block and Block2 as the size of
the second block, the following relationships will be true after the first
block is freed:
mh_First == mh_Lower
mh_Upper == mh_Free + Block2
&mc_Next == mh_First
mc_Next == &mc_Next + Block1 + Block2
mc_Bytes == Block1
mc_Next->mc_Next == NULL
mc_Next->mc_Bytes == mh_Free - (Block1 + Block2)
When the last block is freed mh_Free will equal the difference between
mh_Upper and mh_Lower, mc_Next will be set to NULL, and mc_Bytes will be the
same as mh_Free. In other words, the region will be restored to its
original configuration with a single chunk that contains all the memory in
the region.
Chapter 6 of the Rom Kernel Manual: Exec describes the memory
allocation routines Exec provides for programmers. There are three pairs of
routines. AllocMem() and FreeMem() use the system's memory free list to
manage one block per call. AllocEntry() and FreeEntry() process multiple
blocks with a single call. Allocate() and Deallocate() allow you to manage
blocks from a private memory free list.